﻿using System;
namespace Groundwork.Extensions
{
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;

    /// <summary>
    /// Byte Extension Methods.  These provide a quick easy means of converting a byte array into a number of differnet types
    /// </summary>
    public static class Bytes
    {
        /// <summary>
        /// Converts a string into a byte array
        /// </summary>
        /// <param name="value">string to convert</param>
        /// <returns>byte array</returns>
        /// <example>
        ///   <code>
        ///   <![CDATA[
        /// string myString = "hello world";
        /// byte[] convertedString = myString.ToByteArray();
        /// ]]>
        ///   </code>
        ///   </example>
        /// <remarks>while it is easy to convert a string into a byte array, this exception makes that a little simpler by putting
        /// the conversion directly at your fingertips.</remarks>
        public static byte[] ToByteArray(this string value)
        {
            return Encoding.UTF8.GetBytes(value);
        }

        /// <summary>
        /// Converts the provided byte array to a UTF-8 encoded string
        /// </summary>
        /// <param name="value">byte array to convert</param>
        /// <returns>UTF-8 string</returns>
        /// <example>
        ///   <code>
        ///   <![CDATA[
        /// string myString = "hello world";
        /// byte[] convertedString = myString.ToByteArray();
        /// string convertedBack = convertedString.AsString();
        /// ]]>
        ///   </code>
        ///   </example>
        public static string AsString(this byte[] value)
        {
            return Encoding.UTF8.GetString(value);
        }

        /// <summary>
        /// Converts the provided byte array to a Stream
        /// </summary>
        /// <param name="value">byte array to convert</param>
        /// <returns>converted to stream</returns>
        /// <example>
        ///   <code>
        ///   <![CDATA[
        /// string myString = "hello world";
        /// byte[] convertedString = myString.ToByteArray();
        /// Stream myStream = convertedString.AsStream();
        /// ]]>
        ///   </code>
        ///   </example>
        public static Stream AsStream(this byte[] value)
        {
            MemoryStream stream = new MemoryStream(value);
            return stream;
        }

        /// <summary>
        /// Protects the provided byte array using the Data Protection API (DPAPI) available in Microsoft Windows 2000 and later operating systems.
        /// </summary>
        /// <param name="value">value to protect</param>
        /// <param name="entropy">optional entropy, used to increase the complexity of the encryption, or null for no additional complexity</param>
        /// <param name="scope">protection scope</param>
        /// <returns>protected byte array</returns>
        /// <exception cref="ArgumentNullException">The value paramter is null or empty</exception>
        /// <exception cref="CryptographicException">Encryption failed</exception>
		/// <exception cref="NotSupportedException">The OS does not support this feature</exception>
		/// <exception cref="OutOfMemoryException">The OS ran out of memory while encrypting the data</exception>		
        /// <example>
        ///   <code>
        ///   <![CDATA[
        /// //Protected with no entropy
        /// internal void Example1()
        /// {
        /// 	string myString = "hello world";
        /// 	byte[] convertedString = myString.AsProtected();
        /// }
        /// 
        /// // protected with additional entropy
        /// internal void Example2()
        /// {
        /// 	string myString = "hello world";
        /// 	byte[] convertedString = myString.AsProtected("some random string");
        /// }
        /// 
        /// // protected with additional entropy & data protection scope
        /// internal void Example2()
        /// {
        /// 	string myString = "hello world";
        /// 	byte[] convertedString = myString.AsProtected("some random string", DataProtectionScope.LocalMachine);
        /// }
        /// ]]>
        ///   </code>
        ///   </example>
        /// <remarks>It is possible to convert your byte array into a protected data byte array using this method. It is recommended that
        /// you add additional entropy to the byte array to increase the protection level.  By default the protection scope is CurrentUser
        /// but this can be altered to LocalMachine.</remarks>
        public static byte[] AsProtected(this byte[] value, string entropy = "", DataProtectionScope scope = DataProtectionScope.CurrentUser)
        {
        	
        	byte[] protectedValue; 
        	try
        	{
        		protectedValue = ProtectedData.Protect(value, entropy.ToByteArray(), scope);
        		return  protectedValue;
        	}
        	catch(CryptographicException exception)
        	{
        		throw;
        	}
        	catch(NotSupportedException exception)
        	{
        		throw;
        	}
        	catch(OutOfMemoryException exception)
        	{
        		throw;
        	}
        }
    }
}
